﻿/*
 * Crée par SharpDevelop.
 * Utilisateur: eldunkerquois
 * Date: 26/06/2011
 * Heure: 09:31
 * 
 * Pour changer ce modèle utiliser Outils | Options | Codage | Editer les en-têtes standards.
 */
using System;
using System.Collections.Generic;
using Db4objects.Db4o;
using Db4objects.Db4o.Config;
using Db4objects.Db4o.Constraints;
using openxml.butter.definition;

using StudioLib;

namespace openxml.butter.controller
{
	/// <summary>
	/// Accès aux données ( est ce utile avec Linq ).
	/// A conserver pour les cas complexes ( transaction par exemple )
	/// </summary>
	public sealed class DataFactory
	{
		private static DataFactory instance = new DataFactory();
		private IConfiguration _basicConfiguration ;
		
        
		
		public static DataFactory Instance 
		{
			get {
				return instance;
			}
		}
		
		private DataFactory()
		{
			this._basicConfiguration = getBasicConfiguration();
			
		}
		
		#region Configuration and Database Connection
//		public IObjectServer openBasicServer( String filePath )
//		{
//			IObjectServer server = Db4oClientServer.OpenServer( _basicConfiguration, filePath,0);
//			return server;
//		}
		
		
		/// <summary>
		/// Configuration de base
		/// </summary>
		/// <returns></returns>
		public IConfiguration getBasicConfiguration()
		{
			IConfiguration config = Db4oFactory.NewConfiguration();
			
			
			//créer les contraintes d'unicité ( index et contrainte )
			// Document template
			config.ObjectClass(typeof(DocumentTemplate)).ObjectField("code").Indexed(true);
			config.Add(new UniqueFieldValueConstraint(typeof(DocumentTemplate), "code"));
			
			// Data template
			config.ObjectClass(typeof(DataTemplate)).ObjectField("code").Indexed(true);
			config.Add(new UniqueFieldValueConstraint(typeof(DataTemplate), "code"));
			
			return config;
			
		}
		
		
		#endregion
		
		#region Create
		/// <summary>
		/// Créer un élement de données dans une famille
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code"></param>
		/// <param name="description"></param>
		/// <param name="exemple"></param>
		/// <param name="dataTemplateCode"></param>
		/// <returns></returns>
		public DataItem createDataItem( IObjectContainer db , String code , String description ,String exemple, String dataTemplateCode )
		{
			try
			{
				//charger le modèle
				DataTemplate dt = get<DataTemplate>(db,dataTemplateCode);
				
				//si n'existe pas exception
				if ( dt == null )
				{
					throw new Exception("Le modèle de données '" + dataTemplateCode + "' n'existe pas!"  );
				}
				
				
				//vérifier que le dataitem n'existe pas
				DataItem item = getDataItem( db, dataTemplateCode, code );
				
				if ( item != null )
				{
					throw new Exception("Le Data Item '" + code + "' existe déjà au sein du Data Template '" +  dataTemplateCode + "'" );
				}
				
				
				//créer l'item et l'association
				item = new DataItem( code, description , exemple );
				item.DataTemplate = dt;
				item.Uid = Guid.NewGuid().ToString();
				
				//stocker l'item
				db.Store(item);
				
				
				return item;
				
			}
			catch (Exception e)
			{
				Console.WriteLine("Pb lors de la création du Data Item: {0} ", e.Message);
				throw e;
			}
		}
		
		//créer un modèle de document
        public DocumentTemplate createDocumentTemplate(IObjectContainer db, String code, String description)
        {
            try
            {
                //vérifier l'unicité du code
                DocumentTemplate proto = get<DocumentTemplate>(db, code);

                //si existe renvoie une exception
                if (proto != null)
                {
                    throw new Exception("Le DocumentTemplate '" + code + "' existe déjà : " + proto.Description);
                }

                proto = DocumentTemplate.CreateTemplate(code, description);
                db.Store(proto);

                return proto;
            }
            catch (Exception e)
            {
                StudioTrace.WriteLine("Pb lors de la création du DocumentTemplate: " + e.Message);
                throw e;
            }
        }
		
		/// <summary>
		/// Créer un template en vérifiant que le code est unique
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code"></param>
		/// <param name="description"></param>
		/// <returns></returns>
		public T createTemplate<T>(IObjectContainer db, String code , String description) where T:AbstractTemplateDefinition,new()
		{
			
			
			try
			{
				//vérifier l'unicité du code
				T proto = get<T>(db,code );
				
				//si existe renvoie une exception
				if ( proto != null )
				{
					throw new Exception("Le Template '" + code + "' existe déjà : " + proto.Description ) ;
				}
				
				//créer et stocker
				T dt = new T();
				dt.Code = code ;
				dt.Description = description ;
				
				
				db.Store( dt );
				
				return dt;
			}
			catch (Exception e )
			{
				Console.WriteLine("Pb lors de la création du Template: {0} ", e.Message);
				throw e;
			}
		}
		
		
		
		#endregion
		
		
		#region Associer
		
		/// <summary>
		/// Associer un dataitem et un control au sein d'un document
		/// </summary>
		/// <param name="db"></param>
		/// <param name="documentTemplateCode">Code du modèle de document</param>
		/// <param name="controlId">Id du control Word</param>
		/// <param name="itemUid">Identifiant unique du DataItem</param>
		/// <returns></returns>
		public DataControlAssociation associateControlToData( IObjectContainer db ,String documentTemplateCode ,  String controlId , String itemUid )
		{
			try
			{
				//vérifier l'existence des éléments
				DocumentTemplate doc = get<DocumentTemplate>(db , documentTemplateCode );
				
				if ( doc == null )
				{
					String error = "Le DocumentTemplate '" + documentTemplateCode + "' n'existe pas" ;
					throw new Exception( error ) ;
				}
				
				DataItem item = getDataItem( db, itemUid );
				
				if ( item == null )
				{
					String error = "Le DataItem '" + itemUid + "' n'existe pas" ;
					throw new Exception( error ) ;
				}
				
				//essayer l'association
				DataControlAssociation asso=  new DataControlAssociation( doc , controlId , item ); 
				
				//TODO vérifier l'association 
				IObjectSet results = db.QueryByExample( asso );
				
				if ( results.Count > 0 )
				{
					String error = "L'association document-control/dataitem existe déjà" ;
					throw new Exception( error );
				}
				
				//sauver l'asso
				db.Store( asso );
				
				return asso;
				
			}
			catch (Exception e)
			{
				String error = "Erreur lors de l'association d'un control à une donnée:" + e.Message ;
				throw new Exception( error ) ;
			}
		}
		
		
		/// <summary>
		/// Associer un DocumentTemplate existant à un DataTemplate existant
		/// </summary>
		/// <param name="db"></param>
		/// <param name="documentTemplateCode">Code du DocumentTemplate</param>
		/// <param name="datatTemplateCode">Code du DataTemplate</param>
		/// <returns></returns>
		public DocumentDataMember associateTemplate( IObjectContainer db , String documentTemplateCode, String dataTemplateCode )
		{
			try
			{
				//vérifier l'existence des éléments à associer
				DocumentTemplate doc = get<DocumentTemplate>(db , documentTemplateCode );
				
				if ( doc == null )
				{
					String error = "Le DocumentTemplate '" + documentTemplateCode + "' n'existe pas" ;
					throw new Exception( error ) ;
				}
				
				DataTemplate dt = get<DataTemplate>( db , dataTemplateCode ) ;
				
				if ( dt == null )
				{
					String error = "Le DataTemplate '" + dataTemplateCode + "' n'existe pas ";
					throw new Exception( error );
				}
				
				
				//tenter l'ajout
				DocumentDataMember member = new DocumentDataMember( doc , dt ) ;
				
				
				//vérifier l'existence préalable
				IObjectSet results = db.QueryByExample( member ) ;

				if ( results.Count>0 )
				{
					String error = "Les Modèle de document et de donées sont déjà associés";
					throw new Exception( error );
				}
				
				results = null;
				
				//sauver l'association
				db.Store( member );
				
				return member ;
			}
			catch (Exception e)
			{
				String error = "Erreur lors de l'association de modèles de document et de données:" + e.Message ;
				throw new Exception( error ) ;
			}
		}
		
		
		#endregion
		
		
		
		#region Load : Chargement Complet
	
		
		/// <summary>
		/// Charger un DataTemplate et ses items
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code"></param>
		/// <returns></returns>
		public DataTemplate loadDataTemplate( IObjectContainer db , String code )
		{
			try
			{
				DataTemplate dt = get<DataTemplate>( db, code );
				
				if ( dt ==null )
				{
					return null ;
				}
				
				
				return dt;
			}
			catch (Exception e )
			{
				String error = "Pb lors du chargement complet du DataTemplate '" + code + "' : " + e.Message ;
				throw new Exception(error);
			}
		}
		#endregion
		
	
		
		#region Query
		/// <summary>
		/// Renvoie les associations Controls - DataItem d'un modèle de document
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code"></param>
		/// <returns></returns>
		public List<DataControlAssociation> getDocumentAssociations( IObjectContainer db , String code )
		{
			DocumentTemplate doc = get<DocumentTemplate>( db, code );
			
			if ( doc == null )
			{
				String error = "Le document '" + code + "' n'existe pas ";
				throw new Exception( error );
			}
			
			DataControlAssociation proto = new DataControlAssociation();
			proto.DocumentTemplate = doc ;
			
			IObjectSet results = db.QueryByExample( proto ) ;
			List<DataControlAssociation> liste = new List<DataControlAssociation>();
			
			foreach ( DataControlAssociation dca in results )
			{
				liste.Add( dca ); 
			}
			
			return liste;
			
		}
		
		
		/// <summary>
		/// Renvoie la liste des data template associés à un modèle
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code">Code du Modèle de document</param>
		/// <returns></returns>
		public List<DataTemplate> getDocumentDataTemplates( IObjectContainer db , String code )
		{
            StudioTrace.WriteLine("getDocumentDataTemplates:debut appel");
			DocumentTemplate doc = get<DocumentTemplate>( db, code );
			
			if ( doc == null )
			{
				String error = "Le document '" + code + "' n'existe pas ";
				
				throw new Exception( error );
			}

            doc = null;

            StudioTrace.WriteLine("getDocumentDataTemplates:doc trouvé");

            //partir d'une instance simple du DocumentTemplate pour les perfs
            DocumentTemplate doc1 = new DocumentTemplate();
            doc1.Code = code;

			DocumentDataMember proto = new DocumentDataMember( doc1 , null );
            
			IObjectSet results = db.QueryByExample( proto ) ;       
			List<DataTemplate> liste = new List<DataTemplate>();

            
			foreach (DocumentDataMember member in results) 
			{
				
				liste.Add( member.DataTemplate );
			}

            StudioTrace.WriteLine("getDocumentDataTemplates:fin");

			return liste ;			
				
		}		
		
		/// <summary>
		/// Renvoie la liste des DataItem d'un modèle de données
		/// </summary>
		/// <param name="db"></param>
		/// <param name="parentCode"></param>
		/// <returns></returns>
		public List<DataItem> getDataTemplateItems( IObjectContainer db , String parentCode )
		{
			List<DataItem> items = new List<DataItem>();
			
			DataItem proto = new DataItem();
			DataTemplate protoTp = new DataTemplate( parentCode , null);
			proto.DataTemplate = protoTp ;
			
			IObjectSet results = db.QueryByExample( proto);
			
			if ( results.Count == 0 )
			{
				return items ;
			}			
			
			foreach ( DataItem item in results )
			{
				items.Add( item );
			}
			return items;			
		}
		
		/// <summary>
		/// Trouver un item par son code et le code de son parent DataTemplate
		/// </summary>
		/// <param name="db"></param>
		/// <param name="parentCode">Code du DataTemplate</param>
		/// <param name="itemCode">Code de l'Item</param>
		/// <returns></returns>
		public DataItem getDataItem( IObjectContainer db ,String parentCode , String itemCode )
		{
			DataItem proto = new DataItem( itemCode , null,null);
			DataTemplate protoTp = new DataTemplate( parentCode , null);
			proto.DataTemplate = protoTp ;
			
			IObjectSet results = db.QueryByExample( proto);
			
			if ( results.Count == 0 )
			{
				return null ;
			}
			
			DataItem dt = (DataItem) results[0];
			return dt;
			
		}
		
		/// <summary>
		/// Renvoie un data item pour son identifiant unique
		/// </summary>
		/// <param name="db"></param>
		/// <param name="uid"></param>
		/// <returns></returns>
		public DataItem getDataItem( IObjectContainer db, String uid )
		{
			DataItem proto = new DataItem();
			proto.Uid = uid ;
			
			IObjectSet results = db.QueryByExample( proto);
			
			if ( results.Count == 0 )
			{
				return null ;
			}
			
			return (DataItem) results[0];
		}
		
		
		/// <summary>
		/// Permet de récupérer n'importe instance héritant de Template Definition (éviter DataItem, code pas unique ) par son code.
		/// Exception en cas de doublons
		/// </summary>
		/// <param name="db"></param>
		/// <param name="code"></param>
		/// <returns></returns>
		public T get<T>( IObjectContainer db , String code ) where T:AbstractTemplateDefinition,new()
		{
			T proto= new T();
			proto.Code = code;
			
			IObjectSet results = db.QueryByExample( proto );
			
			if ( results.Count == 0)
			{
				return null;
				
			}
			
			if ( results.Count >1 )
			{
				throw new Exception("Plusieurs définitions répondent aux critères" );
			}
			
			T result = (T) results[0];
			
			
			return result;
		}
		
		/// <summary>
		/// Enumère tous les objets d'un type héritant de TemplateDefinition
		/// </summary>
		/// <param name="db"></param>
		/// <returns></returns>
		public List<T> get<T>( IObjectContainer db  ) where T:AbstractTemplateDefinition,new()
		{
			T proto= new T();
			
			IObjectSet results = db.QueryByExample( proto );
			List<T> newRes = new List<T>( );
			
			foreach (T elt in results)
			{
				newRes.Add( elt);
			}
			results = null;
			
			return newRes;
		}
		
		
		#endregion
		
		#region Update
		/// <summary>
		/// mise à jour d'un template
		/// </summary>
		/// <param name="db"></param>
		/// <param name="tpl"></param>
		/// <returns></returns>
		public bool updateDocumentTemplate( IObjectContainer db , DocumentTemplate tpl )
		{
			try
			{
                int size = tpl.BinaryContent.Length;
                StudioTrace.WriteLine("taille du modele saved:" + size);

                if (size != 0)
                {
                    db.Store(tpl);
                    StudioTrace.WriteLine("saved");
                }
                else
                {
                    StudioTrace.WriteLine("Flux vide, pas de svg");
                    return false;
                }
                    
			}
			catch (Exception e )
			{
				String error = "Impossible de sauver le modèle , " + e.Message ;
				StudioTrace.WriteLine( error);
				throw new Exception(error);
			}
			return true;
		}
		#endregion
		
		
		#region Public Methodes, toujours fournir l'objet container, sans le fermer
		
		
		
		
		#endregion
		
		
	}
}
